/*
 * Copyright 2010 - Jeremie Brebec
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.jb.room.server;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Singleton;

@Singleton
public class RoomScope implements Scope {

	final static private Logger logger = LoggerFactory.getLogger(RoomScope.class);
	
  /*
   * (non-Javadoc)
   * @see com.google.inject.Scope#scope(com.google.inject.Key, com.google.inject.Provider)
   */
	@Override
	public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
		return new Provider<T>() {

			@Override
			public T get() {
				return getCurrentRoom().get(key, unscoped);
			}
			
		};
	}
	
	/*
	 * 
	 */
	@Inject
	public void setScheduledExecutor( ScheduledExecutorService executor ) {
		logger.info("Starting room cleaner");
		executor.scheduleAtFixedRate( new Runnable() {
			
			@Override
			public void run() {
				checkRooms();
			}
		}, 1, 1, TimeUnit.HOURS );
	}

	/*
	 * 
	 */
	private void checkRooms() {
		
		//logger.debug("Checking rooms...");
		
		Date checkDate = new Date( new Date().getTime() - 60 * 60 * 1000 );
	
		/*
		 * On récupère toutes les Rooms
		 */
		for( Room room : getRooms() ) {
			
			if (room.getLastActivity().before( checkDate ) ) {
				logger.info("Removing obsolete room {}", room.getName() );
				removeRoom( room.getName() );
			}
		}

	}

	public void setRoom( String room ) {
		currentRoom.set(room);
	}
	
	public String getCurrentRoomName() {
		return currentRoom.get();
	}
		
	/*
	 * 
	 */
	private Room getCurrentRoom() {
		String roomname = currentRoom.get();
		if (roomname == null)
			return NO_ROOM;
		
		Room room;
		synchronized( rooms ) {
			room = rooms.get( roomname );
			if (room == null) {
				room = new Room( roomname );
				logger.debug("Creating new room {}", roomname );
				rooms.put( roomname, room );
			}
		}
		
		return room;
	}
	
	/*
	 * 
	 */
	private ThreadLocal<String> currentRoom = new ThreadLocal<String>() {

		@Override
		protected String initialValue() {
			return null;
		}

		
		
	};
	
	/**
	 * 
	 * @param name
	 */
	public void removeRoom( String name ) {
		synchronized( rooms ) {
			rooms.remove(name);
		}
	}
	
	/**
	 * 
	 * @return
	 */
	public String[] getRoomsName() {
		synchronized( rooms ) {
			return rooms.keySet().toArray( new String[0] );
		}
	}
	
	/**
	 * 
	 * @return
	 */
	public Collection<Room> getRooms() {
		synchronized( rooms ) {
			return new ArrayList<Room>( rooms.values() );
		}
	}

	/*
	 * 
	 */
	public static class Room {
		
		/**
		 * 
		 * @param name
		 */
		public Room( String name ) {
			this.name = name;
		}
		
		/**
		 * 
		 * @return
		 */
		public String getName() {
			return name;
		}
		
		
		@SuppressWarnings("unchecked")
		synchronized public <T> T get(final Key<T> key, final Provider<T> unscoped) {

			lastActivity = new Date();
			
			T current = (T) scopedObjects.get(key);
			if (current == null && !scopedObjects.containsKey(key)) {
				current = unscoped.get();
				scopedObjects.put(key, current );
			}
					
			return current;
		}
		
		/*
		 * 
		 */
		public Date getLastActivity() {
			return lastActivity;
		}
		
		private Map< Key<?>, Object > scopedObjects = new HashMap< Key<?>, Object >();
		private Date lastActivity = new Date();
		private final String name;
	}
	
	private Map< String, Room > rooms = new HashMap< String, Room >();
	private static Room NO_ROOM = new Room("no-room");
}
